/**
 * 
 */
package com.seeyoui.kensite.common.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.log4j.Logger;

/**
 * @author pantao
 *
 */
public class Downloader {

	private static Logger logger = Logger.getLogger(Downloader.class);

	/**
	 * 默认下载目录
	 */
	private static String storageFolder = Values.USER_HOME + Values.SEPARATOR + "util";

	/**
	 * 下载
	 * 
	 * @param storageFolder
	 *            下载到指定目录
	 * @param downloadURL
	 *            下载的URL
	 * @return 是否下载成功
	 */
	public static boolean download1(String storageFolder, String downloadURL) {
		Downloader.storageFolder = storageFolder;
		return download(downloadURL);
	}

	/**
	 * 下载文件
	 * 
	 * @param downloadURL
	 *            下载的URL
	 * @return 是否下载成功
	 */
	public static boolean download(String downloadURL) {
		if (Validate.isHyperLink(downloadURL) && checkDownloadPath()) {
			logger.info("ready for download url: " + downloadURL + " storage in " + storageFolder);
		} else {
			logger.info("url or storage path are invalidated, can't download");
			return false;
		}
		int byteread = 0;
		String log = "download success from url '" + downloadURL + "' to local '";
		try {
			String fname = checkPath(storageFolder + Values.SEPARATOR + FileUtils.getFileName(downloadURL));
			String tmp = fname + ".tmp";
			File file = new File(tmp);
			log += file.getAbsolutePath() + "'";
			URL url = new URL(downloadURL);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(1000 * 6);
			conn.setRequestProperty("Charset", "UTF-8");
			conn.setRequestProperty("User-Agent", Values.USER_AGENT);
			conn.setRequestProperty("Connection", "Keep-Alive");
			conn.setRequestProperty("Accept", "*/*");
			InputStream inStream = conn.getInputStream();
			if (conn.getResponseCode() != Values.RESPONSE_OK || conn.getContentLength() <= 0) {
				return false;
			}
			FileOutputStream fs = new FileOutputStream(file);
			if (!file.exists()) {
				file.createNewFile();
			}
			byte[] buffer = new byte[1024];
			while ((byteread = inStream.read(buffer)) != -1) {
				fs.write(buffer, 0, byteread);
			}
			fs.flush();
			inStream.close();
			fs.close();
			file.renameTo(new File(fname));
			logger.info(log);
			return true;
		} catch (IOException e) {
			log = log.replace("success", "error") + ", message: " + e.getMessage();
			logger.error(log);
			return false;
		}
	}
	
	/**
     * 从网络下载文件，不会出现403的情况
     * @param url 下载链接
     * @param filePathName 保存路径
     */
    public static boolean download(String url, String filePathName) {
    	HttpGet httpget = new HttpGet(url);
    	try (CloseableHttpClient httpclient = getHttpClient(url); CloseableHttpResponse response = httpclient.execute(httpget);) {
			// 伪装成google的爬虫,一般服务器会进行请求的校验，如果不是http请求会进行拦截
			httpget.setHeader("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)");

			File storeFile = new File(filePathName);
			@SuppressWarnings("resource")
			FileOutputStream output = new FileOutputStream(storeFile);

			// 得到网络资源的字节数组,并写入文件
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				InputStream instream = entity.getContent();
				try {
					byte b[] = new byte[1024];
					int j = 0;
					while ((j = instream.read(b)) != -1) {
						output.write(b, 0, j);
					}
					output.flush();
					output.close();
					return true;
				} catch (IOException ex) {
					throw ex;
				} catch (RuntimeException ex) {
					httpget.abort();
					throw ex;
				} finally {
					try {
						instream.close();
						httpget.releaseConnection();
					} catch (Exception ignore) {
					}
				}
			}

		} catch (Exception e) {
			logger.error(e);
		}
		return false;
	}
    
    /**
	 * 创建HttpClient
	 * @return CloseableHttpClient
	 */
	private static CloseableHttpClient getHttpClient(String url) {
		if (url.toLowerCase().startsWith("https")) {
			// https
			return createSSLClientDefault();
		} else {
			// http
			HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
			return httpClientBuilder.build();
		}
	}
	
	/**
	 * 创建SSL请求客户端
	 * @return CloseableHttpClient
	 */
	private static CloseableHttpClient createSSLClientDefault() {
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				//信任所有
				@Override
				public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
					return true;
				}
			}).build();

			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
			return HttpClients.custom().setSSLSocketFactory(sslsf).build();
		} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
			logger.error("createSSLClientDefault error.", e);
		}
		return HttpClients.createDefault();
	}

	/**
	 * 检查文件路径是否存在
	 * 
	 * @param path
	 *            文件路径
	 * @return 文件的绝对路径，如果文件存在，则生成一个新路径
	 */
	private static String checkPath(String path) {
		File file = new File(path);
		int idx = path.lastIndexOf(".");
		String post = "";
		if (idx > -1) {
			post = path.substring(idx);
			path = path.substring(0, idx);
		}
		int i = 0;
		while (file.exists()) {
			file = new File(path + "_" + (++i) + post);
		}
		return file.getAbsolutePath();
	}

	/**
	 * 检测下载目录是否存在，不存在则创建
	 * 
	 * @return {@link Boolean}
	 */
	private static boolean checkDownloadPath() {
		if (org.apache.commons.lang3.StringUtils.isNotEmpty(storageFolder)) {
			File file = new File(storageFolder);
			if (!file.exists()) {
				file.mkdirs();
				logger.info("mkdir '" + storageFolder + "' success");
			}
			return true;
		}
		return false;
	}

	/**
	 * 获取下载目录
	 * 
	 * @return {@link String}
	 */
	public static String getStorageFolder() {
		return storageFolder;
	}

	/**
	 * 设置下载的目录
	 * 
	 * @param storageFolder
	 *            下载目录
	 */
	public static void setStorageFolder(String storageFolder) {
		Downloader.storageFolder = storageFolder;
	}
}
